import { Injectable } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import ILogger from '@/services/logger.service';
import IHelper from '@/services/helper.service';
import axios, { AxiosProxyConfig, AxiosRequestConfig } from 'axios';
import { fetchSource, debugSource } from "./proxy-source"
import { RedisProvider } from '@/core/providers/redis.provider';
import { concatMap, defer, map, Subject, catchError, of, Observable, from, forkJoin, tap } from 'rxjs';
import { IProxy } from '@/typing';
import { ConfigService } from '@nestjs/config';
import { HttpsProxyAgent } from "https-proxy-agent"
import IResponse from '@/services/response.service';

@Injectable()
export class ProxyFetchService {

  updateSub$: Subject<IProxy> = new Subject<IProxy>()
  constructor(
    private readonly redisProvider: RedisProvider,
    private readonly configService: ConfigService,
  ) {
    this.updateSub$.subscribe(
      res => this.handelNewProxy(res)
    )
  }

  handelNewProxy(proxy: IProxy) {
    if (proxy.source === "source-04") {
      console.log("xxxxx", this.getRedisKey(proxy))
    }
    from(this.verifyProxy(proxy)).pipe(
      concatMap(d => {
        return d ? this.saveProxyToDb(d) : of(undefined)
      })
    ).subscribe()
  }

  refreshSource() {
    fetchSource(this.updateSub$)
    return {state: "updating"}
  }

  verifyDb() {
    from(this.redisProvider.getAllKeys()).pipe(
      concatMap(keys =>
        forkJoin([...keys.map(key =>
          from(this.redisProvider.get<IProxy>(key)).pipe(
            concatMap(proxy => {
              if (proxy) {
                return this.verifyProxy(proxy)
              }
              return of(undefined)
            }),
            concatMap(d => {
              return d ? this.saveProxyToDb(d) : of(undefined)
            })
          )
        )])
      ),
    ).subscribe()
    return {state: "verifying"}
  }

  saveProxyToDb(proxy: IProxy): Observable<undefined> {
    return defer(() =>
      this.redisProvider.set(this.getRedisKey(proxy), proxy)
    ).pipe(
      map(() => undefined),
      catchError(() => of(undefined))
    )
  }

  verifyProxy(proxy: IProxy): Observable<IProxy | undefined> {
    const verifyUri = this.configService.get("PROXY_VERIFY_URI", "https://qq.com")
    const timeout: number = (+this.configService.get("PROXY_VERIFY_TIMEOUT", "10")) * 1000
    return defer(() =>
      axios.get(verifyUri, {
        timeout, proxy: this.convertAxiosProxy(proxy)
      })
    ).pipe(
      map(res => {
        return {
          ...proxy,
          last_time: IHelper.timeFormatted()
        } as IProxy
      }),
      concatMap(validProxy => {
        if (!validProxy.region) {
          return this.getRegion(validProxy).pipe(
            tap(region => console.log(`${validProxy.host}  ${region}`)),
            map(region => ({...validProxy, region} as IProxy))
          )
        }
        return of(validProxy)
      }),
      catchError(error => {
        return of(undefined).pipe(
          concatMap(() => this.redisProvider.del(this.getRedisKey(proxy))),
          map(() => undefined)
        )
      })
    )
  }

  getRedisKey(proxy: IProxy): string {
    return `${proxy.host}_${proxy.port}`
  }

  convertAxiosProxy(proxy: IProxy): AxiosProxyConfig {
    return {
      host: proxy.host,
      port: proxy.port,
      protocol: "http",
    }
  }

  resetDb() {
    return from(this.redisProvider.reset()).pipe(
      map(() => IResponse.success(null))
    )
  }

  getRegion(proxy: IProxy): Observable<string> {
    return defer(() =>
      axios.get(`https://searchplugin.csdn.net/api/v1/ip/get?ip=${proxy.host}`, {timeout: 2 * 1000})
    ).pipe(
      map(res => res.data.data.address as string),
      catchError(err => of(""))
    )
  }

  debug() {
    debugSource()
    return this.getRegion({
      host: "140.210.196.193",
      port: 123,
      source: " ",
      region: "",
      last_time: "",
    })
  }
}